home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / xulrunner / python / download_utils.py < prev    next >
Encoding:
Python Source  |  2007-11-12  |  6.9 KB  |  225 lines

  1. # Miro - an RSS based video player application
  2. # Copyright (C) 2005-2007 Participatory Culture Foundation
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  17.  
  18. from os import access, F_OK
  19. from urlparse import urlparse
  20. import os.path
  21. import re
  22. import urllib
  23. import filetypes
  24. import util
  25.  
  26. from util import checkF, checkU, returnsFilename
  27. from platformutils import unicodeToFilename, unmakeURLSafe
  28.  
  29. URIPattern = re.compile(r'^([^?]*/)?([^/?]*)/*(\?(.*))?$')
  30. # filename limits this is mostly for windows where we have a 255 character
  31. # limit on entire pathname
  32. MAX_FILENAME_LENGTH = 100 
  33. MAX_FILENAME_EXTENSION_LENGTH = 50
  34.  
  35. def fixFileURLS(url):
  36.     """Fix file URLS that start with file:// instead of file:///.  Note: this
  37.     breaks for file URLS that include a hostname, but we never use those and
  38.     it's not so clear what that would mean anyway -- file URLs is an ad-hoc
  39.     spec as I can tell.."""
  40.     if url.startswith('file://'):
  41.         if not url.startswith('file:///'):
  42.             url = 'file:///%s' % url[len('file://'):]
  43.         url = url.replace('\\', '/')
  44.     return url
  45.  
  46. def defaultPort(scheme):
  47.     if scheme == 'https':
  48.         return 443
  49.     elif scheme == 'http':
  50.         return 80
  51.     elif scheme == 'file':
  52.         return None
  53.     else:
  54.         if util.chatter:
  55.             print "WARNING: Assuming port 80 for scheme: %s" % scheme
  56.         return 80
  57.  
  58. def parseURL(url):
  59.     url = fixFileURLS(url)
  60.     (scheme, host, path, params, query, fragment) = util.unicodify(list(urlparse(url)))
  61.     # Filter invalid URLs with duplicated ports (http://foo.bar:123:123/baz)
  62.     # which seem to be part of #441.
  63.     if host.count(':') > 1:
  64.         host = host[0:host.rfind(':')]
  65.  
  66.     if scheme == '' and util.chatter:
  67.         print "WARNING: %r has no scheme" % url
  68.  
  69.     if ':' in host:
  70.         host, port = host.split(':')
  71.         try:
  72.             port = int(port)
  73.         except:
  74.             print "DTV: parseURL: WARNING: invalid port for %r" % url
  75.             port = defaultPort(scheme)
  76.     else:
  77.         port = defaultPort(scheme)
  78.  
  79.     host = host.lower()
  80.     scheme = scheme.lower()
  81.  
  82.     path = path.replace('|', ':') 
  83.     # Windows drive names are often specified as "C|\foo\bar"
  84.  
  85.     if path == '' or path[0] != '/':
  86.         path = '/' + path
  87.     elif re.match(r'/[a-zA-Z]:', path):
  88.         # Fix "/C:/foo" paths
  89.         path = path[1:]
  90.     fullPath = path
  91.     if params:
  92.         fullPath += ';%s' % params
  93.     if query:
  94.         fullPath += '?%s' % query
  95.     return scheme, host, port, fullPath
  96.  
  97. def getFileURLPath(url):
  98.     scheme, host, port, path = parseURL(url)
  99.     if scheme != 'file':
  100.         raise ValueError("%r is not a file URL" % url)
  101.     return unmakeURLSafe(path)
  102.  
  103. # If a filename doesn't have an extension, this tries to find a suitable one
  104. # based on the HTTP content-type info and add it if one is available.
  105. def checkFilenameExtension(filename, httpInfo):
  106.     checkF(filename)
  107.     if 'content-type' in httpInfo and not filetypes.isAllowedFilename(filename):
  108.         guessedExt = filetypes.guessExtension(httpInfo['content-type'])
  109.         if guessedExt is not None:
  110.             filename += guessedExt
  111.     return filename
  112.  
  113. ##
  114. # Finds a filename that's unused and similar the the file we want
  115. # to download
  116. @returnsFilename
  117. def nextFreeFilename(name):
  118.     checkF(name)
  119.     if not access(name,F_OK):
  120.         return name
  121.     parts = name.split('.')
  122.     count = 1
  123.     if len(parts) == 1:
  124.         newname = "%s.%s" % (name, count)
  125.         while access(newname,F_OK):
  126.             count += 1
  127.             newname = "%s.%s" % (name, count)
  128.     else:
  129.         parts[-1:-1] = [str(count)]
  130.         newname = '.'.join(parts)
  131.         while access(newname,F_OK):
  132.             count += 1
  133.             parts[-2] = str(count)
  134.             newname = '.'.join(parts)
  135.     return newname
  136.  
  137. ##
  138. # Returns a reasonable filename for saving the given url
  139. @returnsFilename
  140. def filenameFromURL(url, clean=False):
  141.     checkU(url)
  142.     try:
  143.         match = URIPattern.match(url)
  144.         if match is None:
  145.             # This code path will never be executed.
  146.             return unicodeToFilename(url)
  147.         filename = match.group(2)
  148.         query = match.group(4)
  149.         if not filename:
  150.             ret = query
  151.         elif not query:
  152.             ret = filename
  153.         else:
  154.             root, ext = os.path.splitext(filename)
  155.             ret = u"%s-%s%s" % (root, query, ext)
  156.         if ret is None:
  157.             ret = u'unknown'
  158.         if clean:
  159.             return cleanFilename(ret)
  160.         else:
  161.             return unicodeToFilename(ret)
  162.     except:
  163.         return unicodeToFilename(u'unknown')
  164.  
  165. # Given either a filename or a unicode "filename" return a valid clean
  166. # version of it
  167. @returnsFilename
  168. def cleanFilename(filename):
  169.     for char in ':?><|*/\\"\'':
  170.         filename = filename.replace(char,'')
  171.     if len(filename) == 0:
  172.         return unicodeToFilename(u'_')
  173.     if len(filename) > MAX_FILENAME_LENGTH:
  174.         base, ext = os.path.splitext(filename)
  175.         ext = ext[:MAX_FILENAME_EXTENSION_LENGTH]
  176.         base = base[:MAX_FILENAME_LENGTH-len(ext)]
  177.         filename = base + ext
  178.     if type(filename) == str:
  179.         return unicodeToFilename(filename.decode('ascii', 'replace'))
  180.     else:
  181.         return unicodeToFilename(filename)
  182.  
  183. # Saves data, returns filename, doesn't write over existing files.
  184. def saveData (target, suggested_basename, data):
  185.     try:
  186.         os.makedirs(target)
  187.     except:
  188.         pass
  189.  
  190.     filename = os.path.join (target, suggested_basename)
  191.  
  192.     try:
  193.         # Write to a temp file.
  194.         tmp_filename = filename + ".part"
  195. #        tmp_filename = shortenFilename (tmp_filename)
  196.         tmp_filename = nextFreeFilename (tmp_filename)
  197.         output = file (tmp_filename, 'wb')
  198.         output.write(data)
  199.         output.close()
  200.     except IOError:
  201.         try:
  202.             os.remove (tmp_filename)
  203.         except:
  204.             pass
  205.         raise
  206.  
  207. #    filename = shortenFilename (filename)
  208.     filename = nextFreeFilename (filename)
  209.     needsSave = True
  210.     try:
  211.         os.remove (filename)
  212.     except:
  213.         pass
  214.  
  215.     os.rename (tmp_filename, filename)
  216.  
  217.     return filename
  218.  
  219. # Filter out all non alpha-numeric characters from a future directory name so we 
  220. # can create a corresponding directory on disk without bumping into platform 
  221. # specific pathname limitations.
  222. def filterDirectoryName(name):
  223.     return re.sub(r'[^a-zA-Z0-9]', '-', name)
  224.  
  225.